package com.sheepone.util.beanconvert.json;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.sheepone.util.ReflectUtil;
import com.sheepone.util.ArrayUtil;
import com.sheepone.util.anno.Alias;
import com.sheepone.util.anno.Ignore;
import com.sheepone.util.anno.Regex;
import com.sheepone.util.anno.StartWith;
import com.sheepone.util.string.StringUtil;


import java.lang.reflect.Field;
import java.util.*;

/**
 * @author Miss.杨
 * @description
 * @since 2024/3/11 - 21:45
 */
public class JsonFieldCovertMapping {
    private final Map<String, List<String>> mappings;
    private final boolean enableHumpMapping;

    public JsonFieldCovertMapping(JSONArray jsonArray, Object[] objects) {
        this(jsonArray.getJSONObject(0), objects[0], new String[]{Ignore.ANY_ENVIRONMENT}, true);
    }

    public JsonFieldCovertMapping(JSONArray jsonArray, Object[] objects, String[] environments) {
        this(jsonArray.getJSONObject(0), objects[0], environments, true);
    }

    public JsonFieldCovertMapping(JSONArray jsonArray, Object[] objects, boolean enableHumpMapping) {
        this(jsonArray.getJSONObject(0), objects[0], new String[]{Ignore.ANY_ENVIRONMENT}, enableHumpMapping);
    }

    public JsonFieldCovertMapping(JSONObject jsonObject, Object object) {
        this(jsonObject, object, new String[]{Ignore.ANY_ENVIRONMENT}, true);
    }

    public JsonFieldCovertMapping(JSONArray jsonArray, Object[] objects, String[] environments, boolean enableHumpMapping) {
        this(jsonArray.getJSONObject(0), objects[0], environments, enableHumpMapping);
    }

    public JsonFieldCovertMapping(JSONObject jsonObject, Object object, String[] environments) {
        this(jsonObject, object, environments, true);
    }

    public JsonFieldCovertMapping(JSONObject jsonObject, Object object, boolean enableHumpMapping) {
        this(jsonObject, object, new String[]{Ignore.ANY_ENVIRONMENT}, enableHumpMapping);
    }

    public JsonFieldCovertMapping(JSONObject jsonObject, Object object, String[] environments, boolean enableHumpMapping) {
        Class<?> clazz = object.getClass();
        Set<String> nameSet = jsonObject.keySet();
        Field[] fields = ReflectUtil.accessibleDeclaredFields(clazz);
        this.mappings = new HashMap<>();
        this.enableHumpMapping = enableHumpMapping;
        for (Field field : fields) {
            if (isIgnore(environments, field)) {
                continue;
            }
            // 初始化结果集集合
            List<String> valueStrs = new ArrayList<>();
            // 处理 @StartWith
            processStartWith(field, nameSet, valueStrs);

            // 处理 @Regex
            processRegex(field, nameSet, valueStrs);

            // 先处理可能是复合类型的相关注解，再处理只是单一类型的注解
            // 处理 @Alias
            processAlias(field, nameSet, valueStrs);

            // 直接根据名称匹配
            if(valueStrs.isEmpty()) {
                processOriginalName(field, nameSet, valueStrs);
            }

            // 处理驼峰映射
            if (valueStrs.isEmpty() && enableHumpMapping) {
                processHumpName(field, nameSet, valueStrs);
            }
            // 结果集去重
            valueStrs = ArrayUtil.removeDuplicates(valueStrs);
            if (!valueStrs.isEmpty()) {
                mappings.put(field.getName(), valueStrs);
            }
        }
    }

    private static boolean isIgnore(String[] environments, Field field) {
        Ignore ignore = field.getDeclaredAnnotation(Ignore.class);
        return ignore != null && ArrayUtil.isIntersecting(environments, ignore.environments());
    }

    public List<String> mappingValues(String fieldName) {
        return mappings.get(fieldName);
    }

    public boolean isEnableHumpMapping() {
        return enableHumpMapping;
    }

    private void processHumpName(Field field, Set<String> nameSet, List<String> valueStrs) {
        String fieldName = field.getName();
        for (String name : nameSet) {
            if (StringUtil.underscoreToCamelCase(name).equals(fieldName)) {
                valueStrs.add(name);
                return;
            }
        }
    }

    private void processOriginalName(Field field, Set<String> nameSet, List<String> valueStrs) {
        String fieldName = field.getName();
        for (String name : nameSet) {
            if (name.equals(fieldName)) {
                valueStrs.add(name);
                return;
            }
        }
    }

    private void processAlias(Field field, Set<String> nameSet, List<String> valueStrs) {
        Alias aliasAnno = field.getDeclaredAnnotation(Alias.class);
        if (aliasAnno == null) {
            return;
        }
        String[] aliaes = ArrayUtil.removeDuplicates(aliasAnno.value());
        for (String name : nameSet) {
            for (String alia : aliaes) {
                if (name.equals(alia)) {
                    valueStrs.add(name);
                }
            }
        }
    }

    private void processRegex(Field field, Set<String> nameSet, List<String> valueStrs) {
        Regex regexAnno = field.getDeclaredAnnotation(Regex.class);
        if (regexAnno == null) {
            return;
        }
        String regex = regexAnno.value();
        for (String name : nameSet) {
            if (name.matches(regex)) {
                valueStrs.add(name);
            }
        }
    }

    private void processStartWith(Field field, Set<String> nameSet, List<String> valueStrs) {
        StartWith startWith = field.getDeclaredAnnotation(StartWith.class);
        if (startWith == null) {
            return;
        }
        String[] values = ArrayUtil.removeDuplicates(startWith.value());
        for (String value : values) {
            for (String name : nameSet) {
                if (name.startsWith(value)) {
                    valueStrs.add(name);
                }
            }
        }
    }

}
